﻿namespace NJQuery {
	using System;
	using System.Collections.Specialized;
	using System.Web.UI;
	using System.Web.UI.WebControls;
	using System.ComponentModel;
	using System.Globalization;
	using System.Diagnostics.CodeAnalysis;

	/// <summary>
	/// Base server control used to generate the core functionality of a <see href="http://jqueryui.com/demos/datepicker/">jQuery UI Datepicker widget</see>.
	/// </summary>
	[ParseChildren(true), PersistChildren(false)]
	public abstract class DatePickerBase : ManagedWebControl, IPostBackDataHandler, IPostBackEventHandler {


		#region Constants: Property defaults

		private const bool defaultChangeMonth = false;
		private const bool defaultChangeYear = false;
		private const string defaultCurrentText = "Today";
		private const int defaultFirstDay = 0;
		private const bool defaultHideIfNoPrevNext = false;
		private const bool defaultIsRtl = false;
		private const bool defaultNavigationAsDateFormat = false;
		private const string defaultNextText = "Next>";
		private const string defaultNumberOfMonthsString = "1";
		private const string defaultPrevText = "<Prev";
		private const bool defaultShowButtonPanel = false;
		private const int defaultShowCurrentAtPos = 0;
		private const bool defaultShowMonthAfterYear = false;
		private const bool defaultShowOtherMonths = false;
		private const int defaultStepMonths = 1;
		private const string defaultYearRangeString = "-10:+10";

		#endregion


		#region Static Fields: Property defaults

		//If changing these values, be sure to change the corresponding string constant to match
		private static readonly NumberOfMonths defaultNumberOfMonths = NumberOfMonths.Single;
		private static readonly RelativeValueRange defaultYearRange = new RelativeValueRange(true, -10, 10);

		#endregion


		#region Fields

		private DayNameCollection dayNames;
		private DayNameCollection dayNamesShort;
		private DayNameCollection dayNamesMin;
		private MonthNameCollection monthNames;
		private MonthNameCollection monthNamesShort;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="DatePickerBase"/> class.
		/// </summary>
		/// <param name="tag">The HTML tag to be rendered to the client by this server control.</param>
		protected DatePickerBase(HtmlTextWriterTag tag)
			: base(tag) {
			dayNames = new DayNameCollection(DayNameLength.Normal);
			dayNamesShort = new DayNameCollection(DayNameLength.Short);
			dayNamesMin = new DayNameCollection(DayNameLength.Minimum);
			monthNames = new MonthNameCollection(MonthNameLength.Normal);
			monthNamesShort = new MonthNameCollection(MonthNameLength.Short);
		}

		#endregion


		#region Properties: Widget initialization options

		/// <summary>
		/// The jQuery selector for another field that is to be updated with the selected date from the datepicker. Use the <see cref="AltFormat"/> property to change the format of the date within this field. Leave as blank for no alternate field.
		/// This property maps directly to the "altField" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="AltFormat"/>
		[DefaultValue(""), Category("Data")]
		public virtual string AltField {
			get { return ViewState["AltField"] as string ?? string.Empty; }
			set { ViewState["AltField"] = value; }
		}

		/// <summary>
		/// The JavaScript date format to be used for the field specified in the <see cref="AltField"/> property. This allows one date format to be shown to the user for selection purposes, while a different format is actually sent behind the scenes.
		/// This property maps directly to the "altFormat" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="AltField"/>
		[DefaultValue(""), Category("Data")]
		public virtual string AltFormat {
			get { return ViewState["AltFormat"] as string ?? string.Empty; }
			set { ViewState["AltFormat"] = value; }
		}

		/// <summary>
		/// Allows you to change the month by selecting from a drop-down list. You can disable this feature by setting the property to false.
		/// This property maps directly to the "changeMonth" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="ChangeYear"/>
		[DefaultValue(defaultChangeMonth), Category("Behavior")]
		public virtual bool ChangeMonth {
			get { return ViewState["ChangeMonth"] as bool? ?? defaultChangeMonth; }
			set { ViewState["ChangeMonth"] = value; }
		}

		/// <summary>
		/// Allows you to change the year by selecting from a drop-down list. You can disable this feature by setting the property to false.
		/// This property maps directly to the "changeYear" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="ChangeMonth"/>
		[DefaultValue(defaultChangeYear), Category("Behavior")]
		public virtual bool ChangeYear {
			get { return ViewState["ChangeYear"] as bool? ?? defaultChangeYear; }
			set { ViewState["ChangeYear"] = value; }
		}

		/// <summary>
		/// The text to display for the current day link. This attribute is one of the regionalisation attributes.
		/// This property is HTML encoded and mapped to the "currentText" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultCurrentText), Category("Appearance")]
		public virtual string CurrentText {
			get { return ViewState["CurrentText"] as string ?? defaultCurrentText; }
			set { ViewState["CurrentText"] = value; }
		}

		//TODO: Add cref="DateFormat"
		/// <summary>
		/// A list of long day names for use as requested via the DateFormat setting. They also appear as popup hints when hovering over the corresponding column headings. This attribute is one of the regionalisation attributes.
		/// This property is converted and mapped to the "dayNames" option when the jQuery UI widget is initialized.
		/// </summary>
		[Category("Appearance"), MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual DayNameCollection DayNames {
			get { return this.dayNames; }
		}

		//TODO: Add cref="DateFormat"
		/// <summary>
		/// A list of abbreviated day names for use as requested via the DateFormat property. This attribute is one of the regionalisation attributes.
		/// This property is converted and mapped to the "dayNamesShort" option when the jQuery UI widget is initialized.
		/// </summary>
		[Category("Appearance"), MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual DayNameCollection DayNamesShort {
			get { return this.dayNamesShort; }
		}

		/// <summary>
		/// A list of minimised day names, starting from Sunday, for use as column headers within the datepicker. This attribute is one of the regionalisation attributes.
		/// This property is converted and mapped to the "dayNamesMin" option when the jQuery UI widget is initialized.
		/// </summary>
		[Category("Appearance"), MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual DayNameCollection DayNamesMin {
			get { return this.dayNamesMin; }
		}

		/// <summary>
		/// Set the first day of the week: Sunday is 0, Monday is 1, ... This attribute is one of the regionalisation attributes.
		/// This property maps directly to the "firstDay" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Value must be between 0 and 6, inclusive.</exception>
		[DefaultValue(defaultFirstDay), Category("Appearance")]
		public virtual int FirstDay {
			get { return ViewState["FirstDay"] as int? ?? defaultFirstDay; }
			set {
				if(value < 0 || value > 6) throw new ArgumentOutOfRangeException("value", value, "Value must be between 0 and 6, inclusive.");
				ViewState["FirstDay"] = value;
			}
		}

		/// <summary>
		/// Normally the previous and next links are disabled when not applicable (see MinDate/MaxDate). You can hide them altogether by setting this attribute to true.
		/// This property maps directly to the "hideIfNoPrevNext" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="MinDate"/>
		/// <seealso cref="MaxDate"/>
		[DefaultValue(defaultHideIfNoPrevNext), Category("Behavior")]
		[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Prev", Justification = "Exposed as the jQuery option name.")]
		public virtual bool HideIfNoPrevNext {
			get { return ViewState["HideIfNoPrevNext"] as bool? ?? defaultHideIfNoPrevNext; }
			set { ViewState["HideIfNoPrevNext"] = value; }
		}

		/// <summary>
		/// True if the current language is drawn from right to left. This attribute is one of the regionalisation attributes.
		/// This property maps directly to the "isRTL" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultIsRtl), Category("Behavior")]
		[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Rtl", Justification = "Exposed as the jQuery option name.")]
		public virtual bool IsRtl {
			get { return ViewState["IsRtl"] as bool? ?? defaultIsRtl; }
			set { ViewState["IsRtl"] = value; }
		}

		/// <summary>
		/// Sets a maximum selectable date.
		/// When specified, this property creates a JavaScript Date object mappped to the "maxDate" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="MinDate"/>
		[DefaultValue(null), Category("Behavior")]
		public virtual DateTime? MaxDate {
			get { return ViewState["MaxDate"] as DateTime?; }
			set { ViewState["MaxDate"] = value; }
		}

		/// <summary>
		/// Sets a minimum selectable date.
		/// When specified, this property creates a JavaScript Date object mappped to the "maxDate" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <seealso cref="MaxDate"/>
		[DefaultValue(null), Category("Behavior")]
		public virtual DateTime? MinDate {
			get { return ViewState["MinDate"] as DateTime?; }
			set { ViewState["MinDate"] = value; }
		}

		//TODO: Add cref="DateFormat"
		/// <summary>
		/// The list of full month names, as used in the month header on each datepicker and as requested via the DateFormat property. This attribute is one of the regionalisation attributes.
		/// This property is converted and mapped to the "monthNames" option when the jQuery UI widget is initialized.
		/// </summary>
		[Category("Appearance"), MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual MonthNameCollection MonthNames {
			get { return this.monthNames; }
		}

		//TODO: Add cref="DateFormat"
		/// <summary>
		/// The list of abbreviated month names, for use as requested via the DateFormat property. This attribute is one of the regionalisation attributes.
		/// This property is converted and mapped to the "monthNamesShort" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <exception cref="ArgumentException">There must be twelve items separated by vertical bars.</exception>
		[Category("Appearance"), MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual MonthNameCollection MonthNamesShort {
			get { return this.monthNamesShort; }
		}

		/// <summary>
		/// When true the FormatDate function is applied to the <see cref="PrevText"/>, <see cref="NextText"/>, and <see cref="CurrentText"/> values before display, allowing them to display the target month names for example.
		/// This property maps directly to the "navigationAsDateFormat" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultNavigationAsDateFormat), Category("Appearance")]
		public virtual bool NavigationAsDateFormat {
			get { return ViewState["NavigationAsDateFormat"] as bool? ?? defaultNavigationAsDateFormat; }
			set { ViewState["NavigationAsDateFormat"] = value; }
		}

		/// <summary>
		/// The text to display for the next month link. This attribute is one of the regionalisation attributes.
		/// This property maps directly to the "nextText" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultNextText), Category("Appearance")]
		public virtual string NextText {
			get { return ViewState["NextText"] as string ?? defaultNextText; }
			set { ViewState["NextText"] = value; }
		}

		/// <summary>
		/// Sets how many months to show at once.
		/// This property maps directly to the "numberOfMonths" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(NumberOfMonths), defaultNumberOfMonthsString), Category("Appearance")]
		public virtual NumberOfMonths NumberOfMonths {
			get { return ViewState["NumberOfMonths"] as NumberOfMonths? ?? defaultNumberOfMonths; }
			set { ViewState["NumberOfMonths"] = value; }
		}

		/// <summary>
		/// The text to display for the previous month link. This attribute is one of the regionalisation attributes.
		/// This property maps directly to the "nextText" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultPrevText), Category("Appearance")]
		[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Prev", Justification = "Exposed as the jQuery option name.")]
		public virtual string PrevText {
			get { return ViewState["PrevText"] as string ?? defaultPrevText; }
			set { ViewState["PrevText"] = value; }
		}

		/// <summary>
		/// Whether to show the button panel.
		/// This property maps directly to the "showButtonPanel" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultShowButtonPanel), Category("Appearance")]
		public virtual bool ShowButtonPanel {
			get { return ViewState["ShowButtonPanel"] as bool? ?? defaultShowButtonPanel; }
			set { ViewState["ShowButtonPanel"] = value; }
		}

		/// <summary>
		/// Specify where in a multi-month display the current month shows, starting from 0 at the top/left.
		/// This property maps directly to the "showCurrentAtPos" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Value cannot be negative.</exception>
		[DefaultValue(defaultShowCurrentAtPos), Category("Behavior")]
		public virtual int ShowCurrentAtPos {
			get { return ViewState["ShowCurrentAtPos"] as int? ?? defaultShowCurrentAtPos; }
			set {
				if(value < 0) throw new ArgumentOutOfRangeException("value", value, "Value cannot be negative.");

				ViewState["ShowCurrentAtPos"] = value;
			}
		}

		/// <summary>
		/// Whether to show the month after the year in the header.
		/// This property maps directly to the "showMonthAfterYear" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultShowMonthAfterYear), Category("Appearance")]
		public virtual bool ShowMonthAfterYear {
			get { return ViewState["ShowMonthAfterYear"] as bool? ?? defaultShowMonthAfterYear; }
			set { ViewState["ShowMonthAfterYear"] = value; }
		}

		/// <summary>
		/// Whether or not to display dates in other months (non-selectable) at the start or end of the current month.
		/// This property maps directly to the "showOtherMonths" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultShowOtherMonths), Category("Appearance")]
		public virtual bool ShowOtherMonths {
			get { return ViewState["ShowOtherMonths"] as bool? ?? defaultShowOtherMonths; }
			set { ViewState["ShowOtherMonths"] = value; }
		}

		/// <summary>
		/// Set how many months to move when clicking the Previous/Next links.  This is useful in order to achieve the desired behavior when using multiple rows and/or columns.
		/// This property maps directly to the "stepMonths" option when the jQuery UI widget is initialized.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Value must be positive.</exception>
		/// <seealso cref="NumberOfMonths"/>
		[DefaultValue(defaultStepMonths), Category("Behavior")]
		public virtual int StepMonths {
			get { return ViewState["StepMonths"] as int? ?? defaultStepMonths; }
			set {
				if(value <= 0) throw new ArgumentOutOfRangeException("value", value, "Value must be positive.");
				ViewState["StepMonths"] = value;
			}
		}

		/// <summary>
		/// Controls the range of years displayed in the year drop-down: either relative to current year (-nn:+nn) or absolute (nnnn:nnnn).
		/// This property maps directly to the "yearRange" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(RelativeValueRange), defaultYearRangeString), Category("Behavior")]
		public virtual RelativeValueRange YearRange {
			get { return ViewState["YearRange"] as RelativeValueRange? ?? defaultYearRange; }
			set { ViewState["YearRange"] = value; }
		}

		/// <summary>
		/// Can be a function that takes an input field and current datepicker instance and returns an options object to update the datepicker with. It is called just before the datepicker is displayed.
		/// This is a client-side JavaScript event with the signature function(input).
		/// This property maps directly to the "beforeShow" option when the jQuery UI widget is initialized.
		/// </summary>
		public virtual string OnClientBeforeShow {
			get { return ViewState["OnClientBeforeShow"] as string ?? string.Empty; }
			set { ViewState["OnClientBeforeShow"] = value; }
		}

		/// <summary>
		/// The function takes a date as a parameter and must return an array with [0] equal to true/false indicating whether or not this date is selectable, [1] equal to a CSS class name(s) or '' for the default presentation and [2] an optional popup tooltip for this date. It is called for each day in the datepicker before is it displayed.
		/// This is a client-side JavaScript event with the signature function(date).
		/// This property maps directly to the "beforeShowDay" option when the jQuery UI widget is initialized.
		/// </summary>
		public virtual string OnClientBeforeShowDay {
			get { return ViewState["OnClientBeforeShowDay"] as string ?? string.Empty; }
			set { ViewState["OnClientBeforeShowDay"] = value; }
		}

		/// <summary>
		/// Allows you to define your own event when the datepicker moves to a new month and/or year. The function receives the selected year, month and the datepicker instance as parameters. "this" refers to the associated input field.
		/// This is a client-side JavaScript event with the signature function(year,month,inst).
		/// This property maps directly to the "onChangeMonthYear" option when the jQuery UI widget is initialized.
		/// </summary>
		public virtual string OnClientChangeMonthYear {
			get { return ViewState["OnClientChangeMonthYear"] as string ?? string.Empty; }
			set { ViewState["OnClientChangeMonthYear"] = value; }
		}

		/// <summary>
		/// Allows you to define your own event when the datepicker is selected. The function receives the selected date(s) as text and the datepicker instance as parameters. "this" refers to the associated input field.
		/// This is a client-side JavaScript event with the signature function(dateText,inst).
		/// This property maps directly to the "onSelect" option when the jQuery UI widget is initialized.
		/// </summary>
		public virtual string OnClientSelect {
			get { return ViewState["OnClientSelect"] as string ?? string.Empty; }
			set { ViewState["OnClientSelect"] = value; }
		}

		#endregion


		#region Properties: Additional

		/// <summary>
		/// Gets or sets the value of the date in the input control.
		/// </summary>
		[DefaultValue(typeof(DateTime?), ""), Category("Appearance")]  //TODO: Appearance/Data ??
		public virtual DateTime? SelectedDate {
			get { return ViewState["SelectedDate"] as DateTime?; }
			set { ViewState["SelectedDate"] = value; }
		}

		/// <summary>
		/// Gets or sets whether or not the control performs a postback in the widget's "onSelect" event.
		/// A True value is required in order to receive a <see cref="Select"/> event.
		/// </summary>
		/// <seealso cref="OnClientSelect"/>
		[DefaultValue(false), Category("Behavior")]
		public virtual bool PostBackOnSelect {
			get { return ViewState["PostBackOnSelect"] as bool? ?? false; }
			set { ViewState["PostBackOnSelect"] = value; }
		}

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the displayed year.
		/// </summary>
		public virtual string DisplayedYearClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_DisplayedYear", ClientID); }
		}

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the displayed month.
		/// </summary>
		public virtual string DisplayedMonthClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_DisplayedMonth", ClientID); }
		}

		#endregion


		#region Properties: Protected

		/// <summary>
		/// Gets the event identifier used when posted back in the client's "onSelect" event.
		/// </summary>
		protected virtual string SelectEventArgIdentifier {
			get { return "Select"; }
		}

		#endregion


		#region Events

		/// <summary>
		/// Occurs on postback when the <see cref="SelectedDate"/> property value has changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler SelectedDateChanged;

		/// <summary>
		/// This event is triggered when a date is selected.
		/// This event requires the <see cref="PostBackOnSelect" /> property to have a value of True.
		/// The ASP.NET postback will be appended to any <see cref="OnClientSelect"/> code and mapped to the "onSelect" option when the jQuery UI widget is initialized.
		/// </summary>
		[Category("Action")]
		public event EventHandler Select;

		#endregion


		#region Methods: Event hooks

		/// <summary>
		/// Raises the <see cref="SelectedDateChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnSelectedDateChanged(EventArgs e) {
			if(SelectedDateChanged != null) SelectedDateChanged.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="Select"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnSelect(EventArgs e) {
			if(Select != null) Select.Invoke(this, e);
		}

		#endregion


		#region Methods: Rendering hooks

		/// <summary>
		/// Raises the <see cref="Control.PreRender"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object containing the event data.</param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			if(Visible) Page.RegisterRequiresPostBack(this);
		}

		/// <summary>
		/// Allows a jQuery server control to reference scripts and stylesheets necessary to render their client markup and widgets.
		/// </summary>
		/// <param name="manager">The <see cref="Manager"/> used to handle style and script referencing in addition to unified generation of load and ready scripts.</param>
		protected override void Register(Manager manager) {

			manager.RegisterClientScript(ClientScript.Core);
			manager.RegisterClientScript(ClientScript.UICore);
			manager.RegisterClientScript(ClientScript.UIWidgetFactory);
			manager.RegisterClientScript(ClientScript.UIDatePickerWidget);

			manager.RegisterClientStyle(ClientStyle.UICore);
			manager.RegisterClientStyle(ClientStyle.UIDatePickerWidget);
			manager.RegisterTheme();

			manager.RegisterForReadyScript(this);
		}

		/// <summary>
		/// Renders the jQuery "Ready Event" script required to generate the client-side jQuery widget.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected override void RenderReadyScript(ScriptBuilder script) {
			RenderInitScript(script);
			if(!IsEnabled) RenderDisableScript(script);
		}

		/// <summary>
		/// Renders the jQuery UI initialization script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderInitScript(ScriptBuilder script) {

			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".datepicker(");

			PropertyCollection properties = script.CreateProperties();
			AddInitProperties(properties);
			script.RenderProperties(properties);

			script.Append(");");
		}

		/// <summary>
		/// Creates the properties object that will be rendered as the jQuery UI initialization options.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="properties">A <see cref="PropertyCollection"/> object that will become the jQuery UI initialization options.</param>
		protected virtual void AddInitProperties(PropertyCollection properties) {

			string altField = AltField ?? string.Empty;
			if(altField.Length != 0) properties.AddValue("altField", altField);

			string altFormat = AltFormat ?? string.Empty;
			if(altFormat.Length != 0) properties.AddValue("altFormat", altFormat);

			bool changeMonth = ChangeMonth;
			if(changeMonth) properties.AddValue("changeMonth", changeMonth);

			bool changeYear = ChangeYear;
			if(changeYear) properties.AddValue("changeYear", changeYear);

			string currentText = CurrentText ?? string.Empty;
			if(currentText != defaultCurrentText) properties.AddValue("currentText", currentText, true);

			//TODO: Bridge .NET to JavaScript DateFormat
			//string dateFormat = DateFormat ?? string.Empty;
			//if(dateFormat != defaultDateFormat) properties.AddValue("dateFormat", Manager.GetJavaScriptDateFormat(dateFormat));

			if(DayNames.Culture != Culture.Invariant)
				properties.AddValue("dayNames", dayNames.ToArray());

			if(DayNamesMin.Culture != Culture.Invariant)
				properties.AddValue("dayNamesMin", dayNamesMin.ToArray());

			if(DayNamesShort.Culture != Culture.Invariant)
				properties.AddValue("dayNamesShort", dayNamesShort.ToArray());

			int firstDay = FirstDay;
			if(firstDay != defaultFirstDay) properties.AddValue("firstDay", firstDay);

			bool isRtl = IsRtl;
			if(isRtl != defaultIsRtl) properties.AddValue("isRTL", isRtl);

			DateTime? maxDate = MaxDate;
			if(maxDate.HasValue) properties.AddValue("maxDate", maxDate.Value, false);

			DateTime? minDate = MinDate;
			if(minDate.HasValue) properties.AddValue("minDate", minDate.Value, false);

			if(MonthNames.Culture != Culture.Invariant)
				properties.AddValue("monthNames", monthNames.ToArray());

			if(MonthNamesShort.Culture != Culture.Invariant)
				properties.AddValue("monthNamesShort", monthNamesShort.ToArray());

			bool navigationAsDateFormat = NavigationAsDateFormat;
			if(navigationAsDateFormat != defaultNavigationAsDateFormat) properties.AddValue("navigationAsDateFormat", navigationAsDateFormat);

			string nextText = NextText ?? string.Empty;
			if(nextText != defaultNextText) properties.AddValue("nextText", nextText);

			NumberOfMonths numberOfMonths = NumberOfMonths;
			if(numberOfMonths != defaultNumberOfMonths) properties.AddValue("numberOfMonths", numberOfMonths);

			string prevText = PrevText ?? string.Empty;
			if(prevText != defaultPrevText) properties.AddValue("prevText", prevText);

			bool showButtonPanel = ShowButtonPanel;
			if(showButtonPanel != defaultShowButtonPanel) properties.AddValue("showButtonPanel", showButtonPanel);

			int showCurrentAtPos = ShowCurrentAtPos;
			if(showCurrentAtPos != defaultShowCurrentAtPos) properties.AddValue("showCurrentAtPos", showCurrentAtPos);

			bool showMonthAfterYear = ShowMonthAfterYear;
			if(showMonthAfterYear != defaultShowMonthAfterYear) properties.AddValue("showMonthAfterYear", showMonthAfterYear);

			bool showOtherMonths = ShowOtherMonths;
			if(showOtherMonths != defaultShowOtherMonths) properties.AddValue("showOtherMonths", showOtherMonths);

			int stepMonths = StepMonths;
			if(stepMonths != defaultStepMonths) properties.AddValue("stepMonths", stepMonths);

			RelativeValueRange yearRange = YearRange;
			if(yearRange != defaultYearRange) properties.AddValue("yearRange", yearRange);

			string onClientBeforeShow = OnClientBeforeShow ?? string.Empty;
			if(onClientBeforeShow.Length != 0) properties.AddScript("beforeShow", onClientBeforeShow);

			string onClientBeforeShowDay = OnClientBeforeShowDay ?? string.Empty;
			if(onClientBeforeShowDay.Length != 0) properties.AddScript("beforeShowDay", onClientBeforeShowDay);

			string onClientChangeMonthYear = OnClientChangeMonthYear ?? string.Empty;
			if(onClientChangeMonthYear.Length != 0) properties.AddScript("onChangeMonthYear", onClientChangeMonthYear);

			ScriptBuilder onSelectScript = properties.CreateChildScript();
			RenderOnSelectScript(onSelectScript);
			if(onSelectScript.Length != 0) properties.AddScript("onSelect", onSelectScript);

		}

		/// <summary>
		/// Renders the client "onSelect" event script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderOnSelectScript(ScriptBuilder script) {

			ScriptBuilder additionalOnSelectScript = new ScriptBuilder();
			additionalOnSelectScript.Indent = script.Indent + 1;
			additionalOnSelectScript.OmitIndent();
			RenderAdditionalOnSelectScript(additionalOnSelectScript);

			string onClientSelect = OnClientSelect ?? string.Empty;

			bool hasAdditionalOnSelectScript = additionalOnSelectScript.Length != 0;
			bool hasCustomScript = onClientSelect.Length != 0;
			bool needsPostBackScript = PostBackOnSelect;

			if(!(hasAdditionalOnSelectScript || hasCustomScript || needsPostBackScript)) return;

			if(hasCustomScript && !(hasAdditionalOnSelectScript || needsPostBackScript)) {

				script.NewLine();
				script.Append(onClientSelect);

			} else {

				script.NewLine();
				script.Append("function(dateText, inst) {");

				script.Indent++;

				//Render additional script
				if(hasAdditionalOnSelectScript) {
					script.NewLine();
					script.Append(additionalOnSelectScript.ToString());
				}

				//Render custom OnSelect event
				if(hasCustomScript) {
					script.NewLine();
					script.Append("var onSelect = ");
					script.Append(onClientSelect);
					script.Append(";");
					script.NewLine();
					script.Append("onSelect(dateText, inst);");
				}

				//Render Postback script
				if(PostBackOnSelect) {

					string uniqueID = UniqueID;
					string identifier = SelectEventArgIdentifier;

					script.NewLine();
					script.Append("__doPostBack("); //TODO: Decide if there is some delegate way to as .NET for the postback function and still generate code in event argument
					script.AppendValue(uniqueID);
					script.Append(", ");
					script.AppendValue(identifier);
					script.Append(");");

					Page.ClientScript.RegisterForEventValidation(uniqueID, identifier);

				}

				script.Indent--;
				script.NewLine();

				script.Append("}");
			}
		}

		/// <summary>
		/// Renders additional client "onSelect" event script required by the specific control.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderAdditionalOnSelectScript(ScriptBuilder script) { }

		/// <summary>
		/// Renders a client "disable" method call when the <see cref="WebControl.Enabled"/> property is set to false.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <seealso cref="WebControl.Enabled"/>
		protected virtual void RenderDisableScript(ScriptBuilder script) {

			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".datepicker(");
			script.AppendValue("disable");
			script.Append(");");
		}

		#endregion


		#region Methods: ViewState

		/// <summary>
		/// Saves any state that was modified after the <see cref="Control.TrackViewState"/> method was invoked.
		/// </summary>
		/// <returns>An object that contains the current view state of the control; otherwise, if there is no view state associated with the control, a null reference.</returns>
		protected override object SaveViewState() {

			if(dayNames.Culture != Culture.Invariant) ViewState["DayNames"] = dayNames;
			if(dayNamesShort.Culture != Culture.Invariant) ViewState["DayNamesShort"] = dayNamesShort;
			if(dayNamesMin.Culture != Culture.Invariant) ViewState["DayNamesMin"] = dayNamesMin;
			if(monthNames.Culture != Culture.Invariant) ViewState["MonthNames"] = monthNames;
			if(monthNamesShort.Culture != Culture.Invariant) ViewState["MonthNamesShort"] = monthNamesShort;

			return base.SaveViewState();
		}

		/// <summary>
		/// Restores view-state information from a previous request that was saved with the <see cref="SaveViewState"/> method.
		/// </summary>
		/// <param name="savedState">An object that represents the control state to restore.</param>
		protected override void LoadViewState(object savedState) {
			base.LoadViewState(savedState);

			dayNames = ((DayNameCollection)ViewState["DayNames"]) ?? new DayNameCollection(DayNameLength.Normal);
			dayNamesShort = ((DayNameCollection)ViewState["DayNamesShort"]) ?? new DayNameCollection(DayNameLength.Short);
			dayNamesMin = ((DayNameCollection)ViewState["DayNamesMin"]) ?? new DayNameCollection(DayNameLength.Minimum);
			monthNames = ((MonthNameCollection)ViewState["MonthNames"]) ?? new MonthNameCollection(MonthNameLength.Normal);
			monthNamesShort = ((MonthNameCollection)ViewState["MonthNamesShort"]) ?? new MonthNameCollection(MonthNameLength.Short);

		}

		#endregion


		#region Methods: Post data/events

		/// <summary>
		/// Processes postback data for an ASP.NET server control.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="postDataKey">The key identifier for the control.</param>
		/// <param name="postCollection">The collection of all incoming name values.</param>
		/// <returns>True if the server control's state changes as a result of the postback; otherwise, false.</returns>
		protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
			return false;
		}

		/// <summary>
		/// Signals the server control to notify the ASP.NET application that the state of the control has changed.
		/// This method is used primarily by control developers.
		/// </summary>
		[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This method is amazing.  You get a complaint if you don't include the protected one, but when you do, it tells you to use events.  I included it simply because it gives the derrived control a chance to hook deeper.")]
		protected virtual void RaisePostDataChangedEvent() { }

		/// <summary>
		/// Enables a server control to process an event raised when a form is posted to the server.
		/// </summary>
		/// <param name="eventArgument">A <see cref="String"/> that represents an optional event argument to be passed to the event handler.</param>
		[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This method is amazing.  You get a complaint if you don't include the protected one, but when you do, it tells you to use events.  I included it simply because it gives the derrived control a chance to hook deeper.")]
		protected virtual void RaisePostBackEvent(string eventArgument) {

			Page.ClientScript.ValidateEvent(UniqueID, eventArgument);

			if(eventArgument.Equals(SelectEventArgIdentifier, StringComparison.Ordinal))
				OnSelect(EventArgs.Empty);

		}

		#endregion


		#region Explicit Implementation: IPostBackDataHandler

		bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
			return LoadPostData(postDataKey, postCollection);
		}

		void IPostBackDataHandler.RaisePostDataChangedEvent() {
			RaisePostDataChangedEvent();
		}

		#endregion


		#region Explicit Implementation: IPostBackEventHandler

		void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
			RaisePostBackEvent(eventArgument);
		}

		#endregion


	}

}